/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package Main;

import com.indenova.esigna.crypto.pki.ws.GeneraDocumentosWSResponse;
import com.indenova.esigna.crypto.pki.ws.GenerarNumerosSolicitudWSResponse;
import com.indenova.esigna.crypto.pki.ws.GetContratoWSResponse;
import com.indenova.esigna.crypto.pki.ws.GetSolicitudWSResponse;
import com.indenova.esigna.crypto.pki.ws.ObjectRequest;
import com.indenova.esigna.crypto.pki.ws.ObjectResponse;
import com.indenova.esigna.crypto.pki.ws.PkiReniecIntegrationService;
import com.indenova.esigna.crypto.pki.ws.PkiReniecIntegrationService_Service;
import com.indenova.esigna.crypto.pki.ws.Solicitud;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.net.URL;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.util.List;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import org.ejbca.core.protocol.ws.UserData;
import utils.CSRInfoData;
import utils.PKCS10CertificateRequestExt;

/**
 *
 * @author alvaro
 */
public class Main {
    
    private static String HEADER_CSR_PEM = "-----BEGIN NEW CERTIFICATE REQUEST-----\n";
    private static String FOOTER_CSR_PEM = "\n-----END NEW CERTIFICATE REQUEST-----";
    private static String URL_WS = "http://plataforma3.reniec.gob.pe:8181/PortalEmpleado/services/cxf/PkiReniecIntegrationService?wsdl";
    private static long current = System.currentTimeMillis();
    
    public static void main(String[] args) {

        Solicitud peticion = new Solicitud();
        peticion.setDni("29692413");
        String ficha = "SOL-" + current;
        peticion.setFicha(ficha);
        peticion.setTipo("F");
        peticion.setNombres("Alvaro Ernesto");
        peticion.setApellidos("CUNO PARARI");
        peticion.setCelular("992294573");
        peticion.setEmail("alvaroecp@gmail.com");
        peticion.setDepartamento("Lima");
        peticion.setProvincia("Cañete");
        peticion.setDistrito("Imperial");
        peticion.setDireccion("Jirón Chepita Royal 1541");
        peticion.setTelefono("264039159");
        peticion.setOperadorNombres("FRANCISCO PEREZ RODRIGAÑEZ");
        peticion.setAgenciaErep("AGENCIA-EREP-JESUSMARÍA");
        peticion.setComprobante("COMPROBANTE-0123456789");
        principal(peticion);
        
        /*String dni="29692413"; String fichaa="SOL-1419364750417"; String tipo="F";
        reimpresion(dni, fichaa, tipo);*/
    }
    
    private static void principal(Solicitud peticion) {
        
        /// 1. Obtener numeros de solicitudes
        GenerarNumerosSolicitudWSResponse generaNumsSolWSResp = generarNumerosSolicitudes(peticion);
        if (generaNumsSolWSResp.getResult() == 0) {
            System.out.println("\t\tSolicitud OK");
            String idSolAut = generaNumsSolWSResp.getIdSolicitudAut();
            String idSolFir = generaNumsSolWSResp.getIdSolicitudFir();
            System.out.println("\t\t\t " + "SolAut:" + idSolAut + " SolFir:" + idSolFir);
            /// 2. Obtener certificados digitales
            ObjectResponse generacionResp = generarCertificado(peticion, idSolAut, idSolFir);
            if (generacionResp != null && generacionResp.getBase64Cert().size() == 2) {
                System.out.println("\t\tGeneración Certificado OK");
                List<String> certs = generacionResp.getBase64Cert();
                saveToFile("C:\\" + peticion.getFicha() + "-aut.cer", Base64.decode(certs.get(0)));
                saveToFile("C:\\" + peticion.getFicha() + "-fir.cer", Base64.decode(certs.get(1)));
                /// 3. Generar y obtener documentos
                String contrato = "CON-" + current;
                GeneraDocumentosWSResponse docsResponse = generarDocumentos(peticion, contrato);
                if (docsResponse.getResult() == 0) {
                    System.out.println("\t\tDocumentos generados OK");
                    saveToFile("C:\\" + peticion.getFicha() + "-contrato.pdf", Base64.decode(docsResponse.getContrato()));
                    saveToFile("C:\\" + peticion.getFicha() + "-solicitud.pdf", Base64.decode(docsResponse.getSolicitud()));
                } else {
                    System.out.println("Error " + docsResponse.getResult());
                }
            } else {
                System.out.println("\t\tCertificados no generados");
            }
        } else {
            System.out.println("\t\tNros de solicitudes no generados");
        }
    }
    
    private static void reimpresion(String dni, String ficha, String tipo) {
        
        GetContratoWSResponse contratoResponse = obtenerContrato(dni, ficha, tipo);
        if (contratoResponse.getResult() == 0) {
            System.out.println("\t\tObtenido contrato OK");
            saveToFile("C:\\" + ficha + "-contrato.pdf", Base64.decode(contratoResponse.getDocEncoded()));
        } else {
            System.out.println("Error " + contratoResponse.getResult());
        }
        
        GetSolicitudWSResponse solicitudResponse = obtenerSolicitud(dni, ficha, tipo);
        if (solicitudResponse.getResult() == 0) {
            System.out.println("\t\tObtenida solicitud OK");
            saveToFile("C:\\" + ficha + "-solicitud.pdf", Base64.decode(solicitudResponse.getDocEncoded()));
        } else {
            System.out.println("Error " + solicitudResponse.getResult());
        }
    }
    
    private static GenerarNumerosSolicitudWSResponse generarNumerosSolicitudes(Solicitud peticion) {
        PkiReniecIntegrationService_Service service = null;
        PkiReniecIntegrationService ws = null;
        GenerarNumerosSolicitudWSResponse resp = null;
        try {
            System.out.println("generarNrosSolicitudes");
            service = new PkiReniecIntegrationService_Service(new URL(URL_WS));
            ws = service.getPkiReniecIntegrationServicePort();
            resp = ws.generarNumerosSolicitud(peticion);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return resp;
    }
    
    private static ObjectResponse generarCertificado(Solicitud peticion, String solicitudAutenticacion, String solicitudFirma) {
        PkiReniecIntegrationService_Service service = null;
        PkiReniecIntegrationService ws = null;
        ObjectRequest req = null;
        ObjectResponse resp = null;
        UserData userDataAUT = null;
        UserData userDataFIR = null;
        try {
            System.out.println("generarCertificados");
            service = new PkiReniecIntegrationService_Service(new URL(URL_WS));
            ws = service.getPkiReniecIntegrationServicePort();

            req = new ObjectRequest();
            String cnAUT = "CN=" + peticion.getApellidos() + " " + peticion.getNombres() + "(AUT" + peticion.getDni() + ")";
            String cnFIR = "CN=" + peticion.getApellidos() + " " + peticion.getNombres() + "(FIR" + peticion.getDni() + ")";
            String SN = "SN=DNI:" + peticion.getDni();
            String G = "GIVENNAME=" + peticion.getNombres();
            String SURNAME = "SURNAME=" + peticion.getApellidos();
            String L = "L=" + peticion.getDistrito();
            String S = "S=" + peticion.getDepartamento();
            String C = "C=PE";
            
            userDataAUT = new UserData();
            userDataAUT.setNuSolicitud(peticion.getFicha());
            userDataAUT.setUsername("DNI:" + peticion.getDni() + "AUT");
            userDataAUT.setEmail(peticion.getEmail());
            userDataAUT.setSubjectAltName("1.3.6.1.4.1.35300.1.1.2.2.1=EREP:" + solicitudAutenticacion);
            userDataAUT.setSubjectDN(cnAUT + "," + SN + "," + G + "," + SURNAME + "," + L + "," + S + "," + C);
            userDataAUT.setPkcs10Str(generatePKCS10());

            req.getUserDatas().add(userDataAUT);
            
            userDataFIR = new UserData();
            userDataFIR.setNuSolicitud(peticion.getFicha());
            userDataFIR.setUsername("DNI:" + peticion.getDni() + "FIR");
            userDataFIR.setEmail(peticion.getEmail());
            userDataFIR.setSubjectAltName("1.3.6.1.4.1.35300.1.1.2.2.1=EREP:" + solicitudFirma);
            userDataFIR.setSubjectDN(cnFIR + "," + SN + "," + G + "," + SURNAME + "," + L + "," + S + "," + C);
            userDataFIR.setPkcs10Str(generatePKCS10());
            req.getUserDatas().add(userDataFIR);
            resp = ws.generarCertificadoDigitalDniE(req, peticion.getDni(), null);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return resp;
    }
    
    private static GeneraDocumentosWSResponse generarDocumentos(Solicitud peticion, String contrato) {
        PkiReniecIntegrationService_Service service = null;
        PkiReniecIntegrationService ws = null;
        GeneraDocumentosWSResponse resp = null;
        try {
            System.out.println("generarDocumentos");
            service = new PkiReniecIntegrationService_Service(new URL(URL_WS));
            ws = service.getPkiReniecIntegrationServicePort();
            resp = ws.generaDocumentos(peticion.getDni(), peticion.getFicha(), peticion.getTipo(), contrato);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return resp;
    }
    
    private static String generatePKCS10() {
        String result = null;
        KeyPair kp = null;
        KeyPairGenerator kpg = null;
        CSRInfoData csrInfoData = null;
        try {
            registerBCProvider();
            kpg = KeyPairGenerator.getInstance("RSA", "BC");
            kpg.initialize(2048);
            kp = kpg.generateKeyPair();

            csrInfoData = new CSRInfoData("C", "S", "L", "ON", "OU", "N", "E");
            result = createPEMCSR("SHA1WithRSA", kp.getPublic(), kp.getPrivate(), csrInfoData);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return result;
    }
    
    private static boolean registerBCProvider() {
        boolean res = false;
        try {
            if (Security.getProvider("BC") == null) {
                // No se encuentra registrado, lo añadimos
                Security.addProvider(new BouncyCastleProvider());
            }
            res = true;
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return res;
    }
    
    private static String createPEMCSR(String algorithm, PublicKey publicKey, PrivateKey privateKey, CSRInfoData csrData) {
        String result = null;
        byte[] csrDER = null;
        try {
            csrDER = createDERCSR(algorithm, publicKey, privateKey, csrData);
            if (csrDER != null) {
                result = HEADER_CSR_PEM + new String(Base64.encode(csrDER)) + FOOTER_CSR_PEM;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            csrDER = null;
        }

        return result;
    }

    private static byte[] createDERCSR(String algorithm, PublicKey publicKey, PrivateKey privateKey, CSRInfoData csrInfoData) {
        byte[] result = null;
        PKCS10CertificateRequestExt certReq = null;
        try {
            X509Name x509Name = new X509Name("C=" + csrInfoData.getCountryName() + ",E=" + csrInfoData.getEmailAddress() + ",L=" + csrInfoData.getLocalityName() + ",ST=" + csrInfoData.getStateOrProvinceName()
                    + ",O=" + csrInfoData.getOrganizationName() + ",OU=" + csrInfoData.getOrganizationUnitName() + ",CN=" + csrInfoData.getCommonName());
            if (x509Name != null) {
                certReq = new PKCS10CertificateRequestExt(algorithm, x509Name, publicKey, privateKey, "BC");
                if (certReq != null) {
                    if (certReq.verify()) {
                        result = certReq.getEncoded();
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return result;
    }
    
    private static void saveToFile(String path, byte[] contentData) {
        FileOutputStream fos = null;
        byte[] buf = null;
        ByteArrayInputStream bis = null;
        int len;
        try {
            fos = new FileOutputStream(path);
            buf = new byte[1024];
            bis = new ByteArrayInputStream(contentData);
            while ((len = bis.read(buf)) > 0) {
                fos.write(buf, 0, len);
            }
            fos.close();
            bis.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    private static GetContratoWSResponse obtenerContrato(String dni, String ficha, String tipoFicha) {
        PkiReniecIntegrationService_Service service = null;
        PkiReniecIntegrationService ws = null;
        GetContratoWSResponse resp = null;
        try {
            System.out.println("obtenerContrato");
            service = new PkiReniecIntegrationService_Service(new URL(URL_WS));
            ws = service.getPkiReniecIntegrationServicePort();
            resp = ws.getContrato(dni, ficha, tipoFicha);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return resp;
    }

    private static GetSolicitudWSResponse obtenerSolicitud(String dni, String ficha, String tipoFicha) {
        PkiReniecIntegrationService_Service service = null;
        PkiReniecIntegrationService ws = null;
        GetSolicitudWSResponse resp = null;
        try {
            System.out.println("obtenerSolicitud");
            service = new PkiReniecIntegrationService_Service(new URL(URL_WS));
            ws = service.getPkiReniecIntegrationServicePort();
            resp = ws.getSolicitud(dni, ficha, tipoFicha);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return resp;
    }
}
